2023/12/233469字符

并发

  • 线程可以理解为轻量级的进程

  • 协程(微线程)可以理解为轻量级的线程

  • 协程的优点:创建上百万个而不会导致系统的资源衰竭;线程和进程最多创建不会超过一万个

goroutine

  • 封装 main 函数的 goroutine 称为主 goroutine;
  • 申请每一个 goroutine 所能申请的栈空间的最大尺寸。在 32 位的计算机系统中最大尺寸是 256MB,64 位的系统中尺寸为 1GB。如果某个 goroutine 的栈空间大于这个限制,那么运行时系统就会引发一个栈溢出的运行时恐慌,随后这个 go 程序也会被终止。
  • 初始化:
    1. 创建一个特殊的 defer 语句,用于在主 goroutine 退出时做必要的善后处理(主 goroutine 也可能非正常结束);
    2. 启动专用于在后台清扫内存垃圾的 goroutine,并设置 GC 可用的标识;
    3. 执行 main 包中的 init 函数;
    4. 执行 main 函数,检查主 goroutine 是否引发了运行时恐慌,并进行必要的处理,最后主 goroutine 会结束自己以及当前进程的运行。

goroutine 底层使用 coroutine 实现并发

  • 用户空间,避免了内核态和用户态的切换导致的成本;
  • 可以由语言和框架进行调度;
  • 更小的栈空间允许创建大量的实例。
package main

import (
    "fmt"
    "time"
)

func main() {
    go numbers()  // go 开启新的协程
    go alphabets()
    time.Sleep(3000 * time.Millisecond)  // 协程守护
    fmt.Println("end")
}

func numbers() {
    for i := 0; i <= 5; i++ {
        time.Sleep(250 * time.Millisecond)
        fmt.Printf("%d", i)
    }
}

func alphabets() {
    for i := 'a'; i < 'e'; i++ {
        time.Sleep(400 * time.Millisecond)
        fmt.Printf("%c", i)
    }
}
//--> 0a12b3c45dend

模拟抢票

不要以共享内存的方式去通信,而是以通信的方式共享内存。保证数据安全

互斥锁

  • 在改变数据的情况下,不能做任何操作
package main

import (
    "fmt"
    "sync"
    "time"
)

var ticket = 10  // 总票数

var wg sync.WaitGroup  // 同步等待对象
var mutex sync.Mutex  // 创建锁

func main() {
    wg.Add(2)
    go saleTickets("通道1")
    go saleTickets("通道2")
    wg.Wait()  // 等待计算器的值为 0
    fmt.Println("end")
}

func saleTickets(name string) {
    for {
        mutex.Lock()  // 加锁
        if ticket <= 0 {
            fmt.Println("not have")
            mutex.Unlock()  // 解锁
            break
        }

        time.Sleep(time.Millisecond)
        fmt.Println(name, ticket)
        ticket--
        mutex.Unlock()
    }
    wg.Done()  // 计数器 -1
}

读写锁

  • 读是可以随便读的,多个 goroutine 同时读
  • 写的时候啥也不能干,不能读也不能写
package main

import (
    "fmt"
    "sync"
    "time"
)

var rwMutex *sync.RWMutex  // 创建锁
var wg *sync.WaitGroup  // 同步等待对象

func main() {
    rwMutex = new(sync.RWMutex)
    wg = new(sync.WaitGroup)

    wg.Add(4)
    go readData(1)
    go writeData(1)
    go readData(2)
    go writeData(2)

    wg.Wait()
    fmt.Println("end")
}

func readData(num int) {
    fmt.Println(num, "read start")
    rwMutex.RLock()  // 读操作上锁
    fmt.Println(num, "reading...")
    time.Sleep(1 * time.Second)
    rwMutex.RUnlock()  // 读操作解锁
    fmt.Println(num, "read over")
    wg.Done()
}

func writeData(num int) {
    fmt.Println("write start")
    rwMutex.Lock()  // 写操作上锁
    fmt.Println("writing...")
    time.Sleep(1 * time.Second)
    rwMutex.Unlock()  // 写操作解锁
    fmt.Println("write over")
    wg.Done()
}